const vscode = require('vscode');
const fs = require('fs');
const util = require('../util');
const fetch = require('node-fetch');
const os = require('os');
const exec = require('child_process').exec;

/**
 * 根据服务名获取指定环境的HSF版本列表
 * @param {*} serviceName 
 * @param {*} env 
 */
function getHsfVersionList(serviceName, env) {
    env = env || 'daily'; // 默认日常环境，线上环境为 online
    const host = util.getHsfHost(env == 'daily');
    let url = `http://${host}/hsfops/envs/${env}/services?filter=service&pattern=${serviceName}*`;
    return new Promise((resolve, reject) => {
        fetch(url).then(resp => resp.json()).then(resp => {
            console.log(resp);
            if(resp && resp.length) {
                const result = resp.map(item => {
                    const temp = item.dataId.split(':');
                    item.serviceNameAndVersion = item.dataId;
                    item.serviceName = temp[0];
                    item.version = temp[1];
                    item.group = item.groupId;
                    item.appname = item.appName;
                    delete item.dataId;
                    delete item.appName;
                    delete item.groupId;
                    return item;
                });
                resolve({code: 0, data: result});
            } else {
                resolve({code: 600, message: `${env}环境中未找到名为 ${serviceName} 的service！`});
            }
        }).catch(e => {
            console.error(e);
            resolve({code: 500, message: e.message});
        });
    });
}

/**
 * 根据服务名和版本号推测 artifactId
 * @param {*} serviceNameAndVersion 
 * @param {*} env 
 */
function getArtifactId(serviceNameAndVersion, env) {
    env = env || 'daily'; // 默认日常环境，线上环境为 online
    const host = util.getHsfHost(env == 'daily');
    let getMetaDataUrl = `http://${host}/hsfops/envs/${env}/services/${serviceNameAndVersion}/metadata`;
    return new Promise((resolve, reject) => {
        fetch(getMetaDataUrl).then(resp => resp.json()).then(resp => {
            console.log(resp);
            if(resp && resp.codeSource) {
                // codeSource 到 artifactId 转换规则不确定
                // cupoo.service.api-1.1.35.jar => cupoo.service.api
                // customer-platform-client-1.0.26.jar => customer-platform-client
                // jar:file:/home/admin/neptunes/target/exploded/BOOT-INF/lib/api.crm.contact-1.0.4.jar!/
                let matchResults = /([\w:-]+?\/)*([\w.-]+)-([\d.]+(-SNAPSHOT)?)\.jar/g.exec(resp.codeSource);
                if (matchResults.length >= 3 && matchResults[2]) {
                    const artifactId = matchResults[2];
                    resolve({code: 0, data: artifactId});
                } else {
                    resolve({code: 601, message: `自动推测artifactId失败：${resp.codeSource}`});
                }
            } else {
                resolve({code: 500, message: `调用接口失败：${getMetaDataUrl}`});
            }
        }).catch(e => {
            console.error(e);
            resolve({code: 500, message: e.message});
        });
    });
}

/**
 * 根据服务名和版本号推测 artifactId
 * @param {*} serviceNameAndVersion 
 * @param {*} env 
 */
function getMethodList(serviceNameAndVersion, env) {
    env = env || 'daily'; // 默认日常环境，线上环境为 online
    const host = util.getHsfHost(env == 'daily');
    let getMetaDataUrl = `http://${host}/hsfops/envs/${env}/services/${serviceNameAndVersion}/metadata`;
    return new Promise((resolve, reject) => {
        fetch(getMetaDataUrl).then(resp => resp.json()).then(resp => {
            if(resp && !resp.code) {
                resolve({code: 0, data: resp});
            } else {
                resolve({code: resp.code || 500, message: resp.message || `调用接口失败：${getMetaDataUrl}`});
            }
        }).catch(e => {
            console.error(e);
            resolve({code: 500, message: e.message});
        });
    });
}


/**
 * 根据 artifactId 获取依赖信息
 * @param {*} artifactId 
 */
function getDependencyInfo(artifactId) {
    const host = util.getAbbHost();
    let url = `http://repo.${host}/nexus/service/local/lucene/search?q=${artifactId}&collapseresults=true`;
    const config = {
        method: 'GET',
        headers: {'accept': 'application/json'}
    };
    return new Promise((resolve, reject) => {
        fetch(url, config).then(resp => resp.json()).then(resp => {
            if(resp && resp.data && resp.data.length) {
                const dependencyInfo = {
                    groupId: resp.data[0].groupId,
                    artifactId: resp.data[0].artifactId,
                    version: resp.data[0].version, // 一般和latestRelease相同
                    latestRelease: resp.data[0].latestRelease, // 最新稳定版
                    latestSnapshot: resp.data[0].latestSnapshot, // 最新开发版
                }
                resolve({code: 0, data: dependencyInfo});
            } else {
                resolve({code: 600, message: `artifactId无效：${artifactId}`});
            }
        }).catch(e => {
            console.error(e);
            resolve({code: 500, message: e.message});
        });
    });
}

function getWebViewContent(context, templatePath) {
    const resourcePath = util.getExtensionFileAbsolutePath(context, templatePath);
    let html = fs.readFileSync(resourcePath, 'utf-8');
    // vscode不支持直接加载本地资源，需要替换成其专有路径格式，这里只是简单的将样式和JS的路径替换
    html = html.replace(/(<link.+?href="|<script.+?src="|<img.+?src=")(.+?)"/g, (m, $1, $2) => $1 + util.getExtensionFileVscodeResource(context, $2.replace('../../', '')) + '"');
    return html;
}

function invokeCallback(panel, message, resp) {
    console.log('回调消息：', resp);
    // 错误码在400-600之间的，默认弹出错误提示
    if (typeof resp == 'object' && resp.code && resp.code >= 400 && resp.code < 600) {
        util.showError(resp.message || '发生未知错误！');
    }
    panel.webview.postMessage({cmd: 'vscodeCallback', cbid: message.cbid, data: resp});
}

function saveAntx(global, proxy) {
    const antxPath = `${global.projectPath}/config/antx.local.properties`;
    const oldAntx = util.readProperties(antxPath);
    const oldAntxArray = [];
    Object.keys(oldAntx).sort().forEach(key => {
        oldAntxArray.push(`${key}.hsf.service.version=${oldAntx[key]}`);
    });
    let antxArray = [];
    proxy.services.forEach(service => {
        antxArray.push(`${service.appname}.hsf.service.version=${service.version}`);
    });
    // 如果内容相同，不写入文件
    if (util.jsonEquals(oldAntxArray, antxArray)) return;
    const antxContent = 
`# 以下配置仅在本地有效，其他环境请在 aone 中配置
# @link https://lark.alipay.com/fd-art/issue/lap25i

${antxArray.join(os.EOL)}
`;
    proxy.services.forEach(service => {
        delete service.version;
    });
    fs.writeFileSync(antxPath, antxContent, 'utf-8');
}

// 保存proxy到单个文件，这个应该不是采用了，废弃之
/* function saveAsSingleFile(global, proxy) {
    const proxyPath = `${global.projectPath}/config/proxy.js`;
    const proxyContent = `module.exports = ${util.formatToSimpleJSON(proxy, '  ', true)};\n`;
    fs.writeFileSync(proxyPath, proxyContent, 'utf-8');
} */

/**
 * 保存proxy到多个文件
 * @param {*} global 
 * @param {*} proxy 
 */
function saveAsMultipleFile(global, proxy) {
    const cfgPath = `${global.projectPath}/config/proxy-cfg`;
    const proxyPath = `${global.projectPath}/config/proxy.js`;
    const oldProxy = util.dynamicRequire(proxyPath);
    const oldProxyMap = {};
    oldProxy.services.forEach(service => oldProxyMap[service.appname] = service);
    if (!fs.existsSync(cfgPath)) {
        fs.mkdirSync(cfgPath);
    }
    proxy.services.forEach(service => {
        // 文件内容没有发生变化时，不修改文件
        if (util.jsonEquals(oldProxyMap[service.appname], service) && 
            fs.existsSync(`${cfgPath}/${service.appname}.js`)) {
            return;
        }
        let content = `module.exports = ${util.formatToSimpleJSON(service, '  ', true)};\n`;
        fs.writeFileSync(`${cfgPath}/${service.appname}.js`, content, 'utf-8');
    });
    
    const proxyContent = 
`/**
* proxy拆分规范：以 hsf 应用名为维度放到 proxy-cfg 目录下
* @type {[*]}
*/
module.exports = {
  group: 'HSF', // 默认的分组，如果下面不设置，服务使用该分组
  mavenType: 'taobao',
  errorAsNull: false, // 是否自动将 hsf/tr 调用异常自动 try catch 返回 null，默认 false
  mavenName: 'dev', // 可选值有 dev, test, mayibank_dev, mayibank_test
  services: require('fs').readdirSync(\`\${__dirname}/proxy-cfg/\`)
    .filter(name => name.match(/\.js$/))
    .map(name => require(\`\${__dirname}/proxy-cfg/\${name}\`)),
};
`;
    fs.writeFileSync(proxyPath, proxyContent, 'utf-8');
}

/**
 * 保存proxy到多个文件
 * @param {*} global 
 * @param {*} proxy 
 */
function saveAsMultipleFile2(global, proxy) {
    const cfgPath = `${global.projectPath}/config/proxy-cfg`;
    if (!fs.existsSync(cfgPath)) {
        fs.mkdirSync(cfgPath);
    }
    proxy.services.forEach(service => {
        let content = `module.exports = ${util.formatToSimpleJSON(service, '  ', true)};\n`;
        fs.writeFileSync(`${cfgPath}/${service.appname}.js`, content, 'utf-8');
    });
    const proxyPath = `${global.projectPath}/config/proxy.js`;
    proxy.services = proxy.services.map(service => {
        return `===require('./proxy-cfg/${service.appname}.js')===`;
    });
    const proxyContent = 
`/**
 * proxy拆分规范：以 hsf 应用名为维度放到 proxy-cfg 目录下
 * @type {[*]}
 */
module.exports = ${util.formatToSimpleJSON(proxy, '  ', true).replace(/'===/g, '').replace(/==='/g, '')};\n
`;
    fs.writeFileSync(proxyPath, proxyContent, 'utf-8');
}

/**
 * 存放所有消息回调函数，根据 message.cmd 来决定调用哪个方法
 */
const messageHandler = {
    // 获取所有 proxy 信息，包括HSF版本
    getAllProxy(global, message) {
        const projectPath = global.projectPath;
        const proxy = util.dynamicRequire(`${projectPath}/config/proxy.js`);
        // const proxy = require(`/Users/liuxianan/workspace/web/vscode-egg-helper/src/test/proxy.js`);
        // 读取本地 antx配置文件
        const content =  fs.readFileSync(`${projectPath}/config/antx.local.properties`, 'utf-8');
        let rows = content.split(os.EOL);
        const versionMap = {};
        rows.forEach(row => {
            const result = /^([^#]+?)\.hsf\.service\.version\s*?=\s*?(.+$)/g.exec(row);
            if (result) {
                versionMap[result[1]] = result[2];
            }
        })
        // 将antx中的version与proxy.js内容合并
        proxy.services.forEach(service => {
            service.version = versionMap[service.appname] || '1.0.0';
        });
        invokeCallback(global.panel, message, proxy);
    },
    getHsfVersionList(global, message) {
        getHsfVersionList(message.serviceName, message.env).then(resp => {
            invokeCallback(global.panel, message, resp);
        });
    },
    getArtifactId(global, message) {
        getArtifactId(message.serviceNameAndVersion, message.env).then(resp => {
            invokeCallback(global.panel, message, resp);
        });
    },
    getDependencyInfo(global, message) {
        getDependencyInfo(message.artifactId).then(resp => {
            invokeCallback(global.panel, message, resp);
        });
    },
    getMethodList(global, message) {
        getMethodList(message.serviceNameAndVersion, message.env).then(resp => {
            invokeCallback(global.panel, message, resp);
        });
    },
    // 弹出提示
    alert(global, message) {
        util.showInfo(message.info);
    },
    // 显示错误提示
    error(global, message) {
        util.showError(message.info);
    },
    // 获取工程名
    getProjectName(global, message) {
        invokeCallback(global.panel, message, util.getProjectName(global.projectPath));
    },
    // 全部保存
    saveAll(global, message) {
        const proxy = message.proxy;
        saveAntx(global, proxy);
        // saveAsSingleFile(global, proxy);
        saveAsMultipleFile(global, proxy);
        invokeCallback(global.panel, message, {code: 0, message: '保存成功！'});
    },
    openFileInFinder(global, message) {
        util.openFileInFinder(message.path);
    },
    openJarByJdGui(global, message) {
        util.openJarByJdGui(`${global.projectPath}/${message.path}`)
    },
    openFileInVscode(global, message) {
        util.openFileInVscode(`${global.projectPath}/${message.path}`, message.text);
    },
    openUrlInBrowser(global, message) {
        util.openUrlInBrowser(message.url);
    },
    addGoldLog(global, message) {
        util.addGoldLog(message.key);
    },
};

module.exports = function(context) {
    // 打开文件时触发
    vscode.workspace.onDidOpenTextDocument(document => {
        if (/config\/proxy\.js$/g.test(document.fileName)) {
            const key = 'eggHelper.showOpenHsfManageTip';
            if (!vscode.workspace.getConfiguration().get(key)) return;
            const tip = '检测到您打开了HSF配置文件，是否需要Egg助手打开可视化配置界面？（后续您仍然可以通过proxy.js右上角扳手图标、或者在资源管理器中右键打开）';
            vscode.window.showInformationMessage(tip, '是', '否', '不再提示').then(result => {
                if (result === '是') {
                    vscode.commands.executeCommand('extension.hsf.openManagePage');
                } else if (result === '不再提示') {
                    // 写入配置文件，注意，默认写入工作区配置，而不是用户配置
                    vscode.workspace.getConfiguration().update(key, false);
                }
            });
        }
    });

    // 注册命令，可以给命令配置快捷键或者右键菜单
    // 回调函数参数uri：当通过资源管理器右键执行命令时会自动把所选资源URI带过来，当通过编辑器中菜单执行命令时，会将当前打开的文档URI传过来
    context.subscriptions.push(vscode.commands.registerCommand('extension.hsf.openManagePage', function (uri) {
        // 工程目录一定要提前获取，因为创建了webview之后activeTextEditor会不准确
        const projectPath = util.getProjectPath(uri);
        if (!projectPath) return;
        if (!fs.existsSync(`${projectPath}/config/proxy.js`)) {
            util.showError('当前不是一个egg工程！');
            return;
        }
        const panel = vscode.window.createWebviewPanel(
            'hsfManagePage', // viewType
            "HSF可视化配置", // 视图标题
            vscode.ViewColumn.One, // 显示在编辑器的哪个部位
            {
                enableScripts: true, // 启用JS，默认禁用
                retainContextWhenHidden: true, // webview被隐藏时保持状态，避免被重置
            }
        );
        let global = { projectPath, panel};
        panel.webview.html = getWebViewContent(context, 'src/view/hsf-manage.html')
        panel.webview.onDidReceiveMessage(message => {
            if (messageHandler[message.cmd]) {
                messageHandler[message.cmd](global, message);
            } else {
                util.showError(`未找到名为 ${message.cmd} 回调方法!`);
            }
        }, undefined, context.subscriptions);
        util.addGoldLog('openHsfManagePage');
    }));
};
